<template>
  <div class="app-container">
    <div v-if="tabsData.length === 0">
      <el-empty description="空" />
    </div>
    <div v-else>
      <el-tabs v-model="tabsActive" type="card" @tab-click="selectTab">
        <el-tab-pane v-for="tabName in tabsData" :key="tabName" :label="tabName" :name="tabName" />
      </el-tabs>
      <el-form :inline="true" :model="searchFormData" class="demo-form-inline">
        <el-form-item label="路由ID">
          <el-input v-model="searchFormData.routeId" placeholder="接口名，支持模糊查询" clearable />
        </el-form-item>
        <el-form-item label="AppId">
          <el-input v-model="searchFormData.appKey" placeholder="AppId，支持模糊查询" clearable />
        </el-form-item>
        <el-form-item label="IP">
          <el-input v-model="searchFormData.limitIp" placeholder="ip，支持模糊查询" clearable />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="Search"  @click="onSearchTable">查询</el-button>
        </el-form-item>
      </el-form>
      <el-button type="primary"  icon="Plus" style="margin-bottom: 10px;" @click="onAdd">新增限流</el-button>
      <el-table
        :data="pageInfo.list"
        border
      >
        <el-table-column
          prop="limitKey"
          label="限流维度"
          width="400"
        >
          <template #default="scope">
            <div v-html="limitRender(scope.row)"></div>
          </template>
        </el-table-column>
        <el-table-column
          prop="limitType"
          label="限流策略"
          width="120"
        >
          <template #header>
            <div class="cell-title">
              限流策略
              <el-popover
                placement="top"
                title="限流策略"
                width="500"
                >
                <template #reference>
                  <el-icon><QuestionFilled /></el-icon>
                </template>
                <template #default>
                  <div>
                    <p>窗口策略：每秒处理固定数量的请求，超出请求数量返回错误信息。</p>
                    <p>令牌桶策略：每秒放置固定数量的令牌数，每个请求进来后先去拿令牌，拿到了令牌才能继续，拿不到则等候令牌重新生成了再拿。</p>
                  </div>
                </template>
              </el-popover>
            </div>
            <!-- <i v-popover:popover class="el-icon-question" style="cursor: pointer"></i> -->
          </template>
          <template #default="scope">
            <span v-if="scope.row.limitType === 1">窗口策略</span>
            <span v-if="scope.row.limitType === 2">令牌桶策略</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="info"
          label="限流信息"
          width="250"
        >
          <template #default="scope">
            <span v-html="infoRender(scope.row)"></span>
          </template>
        </el-table-column>
        <el-table-column
          prop="limitStatus"
          label="状态"
          width="80"
        >
          <template #default="scope">
            <span v-if="scope.row.limitStatus === 1" style="color:#67C23A">已开启</span>
            <span v-if="scope.row.limitStatus === 0" style="color:#909399">已关闭</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="orderIndex"
          label="排序"
          width="80"
        />
        <el-table-column
          prop="remark"
          label="备注"
          width="150"
          :show-overflow-tooltip="true"
        />
        <el-table-column
          prop="gmtCreate"
          label="创建时间"
          width="160"
        />
        <el-table-column
          prop="gmtModified"
          label="修改时间"
          width="160"
        />
        <el-table-column
          label="操作"
          fixed="right"
          width="80"
        >
          <template #default="scope">
            <el-button type="link" text  @click="onTableUpdate(scope.row)">修改</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        background
        style="margin-top: 5px"
        :current-page="searchFormData.pageIndex"
        :page-size="searchFormData.pageSize"
        :page-sizes="[5, 10, 20, 40]"
        :total="pageInfo.total"
        layout="total, sizes, prev, pager, next"
        @size-change="onSizeChange"
        @current-change="onPageIndexChange"
      />
    </div>
    <!-- dialog -->
    <el-dialog
      :title="dlgTitle"
      v-model="limitDialogVisible"
      :close-on-click-modal="false"
      @close="onLimitDialogClose"
    >
      <el-form
        ref="limitDialogForm"
        :model="limitDialogFormData"
        :rules="rulesLimit"
        label-width="150px"

      >
        <el-form-item label="限流维度" prop="typeKey">
          <el-checkbox-group v-model="limitDialogFormData.typeKey">
            <el-checkbox v-model="limitDialogFormData.typeKey[0]" :value="1" name="typeKey">路由ID</el-checkbox>
            <el-checkbox v-model="limitDialogFormData.typeKey[1]" :value="2" name="typeKey">AppId</el-checkbox>
            <el-checkbox v-model="limitDialogFormData.typeKey[2]" :value="3" name="typeKey">IP</el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item v-show="checkTypeKey(1)" prop="routeId" label="路由ID" :rules="checkTypeKey(1) ? rulesLimit.routeId : []">
          <el-select v-model="limitDialogFormData.routeId" filterable placeholder="可筛选" style="width: 300px;">
            <el-option
              v-for="item in routeList"
              :key="item.id"
              :label="item.id"
              :value="item.id"
            >
              <span style="float: left">{{ item.name }}</span>
              <span style="float: right; color: #8492a6; font-size: 13px">{{ item.version }}</span>
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item v-show="checkTypeKey(2)" prop="appKey" label="AppId" :rules="checkTypeKey(2) ? rulesLimit.appKey : []">
          <el-input v-model="limitDialogFormData.appKey" placeholder="需要限流的AppId" />
        </el-form-item>
        <el-form-item v-show="checkTypeKey(3)" label="限流IP" prop="limitIp" :rules="checkTypeKey(3) ? rulesLimit.ip : []">
          <el-input v-model="limitDialogFormData.limitIp" type="textarea" :rows="2" placeholder="多个用英文逗号隔开" />
        </el-form-item>
        <el-form-item label="限流策略">
          <el-radio-group v-model="limitDialogFormData.limitType">
            <el-radio :value="1">窗口策略</el-radio>
            <el-radio :value="2">令牌桶策略</el-radio>
          </el-radio-group>
          <el-popover
            ref="popover"
            placement="top"
            title="限流策略"
            width="500"
            trigger="hover">
            <template #reference>
              <el-icon><QuestionFilled /></el-icon>
            </template>
            <template #default>
              <div>
                <p>窗口策略：每秒处理固定数量的请求，超出请求数量返回错误信息。</p>
                <p>令牌桶策略：每秒放置固定数量的令牌数，每个请求进来后先去拿令牌，拿到了令牌才能继续，拿不到则等候令牌重新生成了再拿。</p>
              </div>
            </template>
          </el-popover>
        </el-form-item>
        <el-form-item label="开启状态">
          <el-switch
            v-model="limitDialogFormData.limitStatus"
            active-color="#13ce66"
            inactive-color="#ff4949"
            :active-value="1"
            :inactive-value="0"
          >
          </el-switch>
        </el-form-item>
        <el-form-item label="排序" prop="orderIndex">
          <el-input-number v-model="limitDialogFormData.orderIndex" controls-position="right" :min="0" />
          <span class="tip" style="margin-left: 10px">值小优先执行</span>
        </el-form-item>
        <el-form-item v-show="isWindowType()" label="请求数" prop="execCountPerSecond" :rules="isWindowType() ? rulesLimit.execCountPerSecond : []">
          每 <el-input-number v-model="limitDialogFormData.durationSeconds" controls-position="right" :min="1" /> 秒可处理
          <el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" /> 个请求
        </el-form-item>
        <el-form-item v-show="isTokenType()" label="令牌桶容量" prop="tokenBucketCount" :rules="isTokenType() ? rulesLimit.tokenBucketCount : []">
          <el-input-number v-model="limitDialogFormData.tokenBucketCount" controls-position="right" :min="1" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="limitDialogFormData.remark" type="textarea" :rows="2" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="limitDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="onLimitDialogSave">保 存</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<style>
  .custom-tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 8px;
  }
  .el-input.is-disabled .el-input__inner {color: #909399;}
  .el-radio__input.is-disabled+span.el-radio__label {color: #909399;}
  .el-table .cell-title{
    display: flex;
    align-items: center;
    i{
      margin-left: 3px;
    }
  }
</style>
<script setup>
import { ref,onMounted, reactive, watch,nextTick } from 'vue';
import { useMessage,useMessageBox } from '@/hooks/message';
import request from '@/utils/request';
const tabsData=ref([])
const tabsActive=ref('first')
const serviceTextLimitSize=20
const filterText=ref('')
const treeData=ref([])
const tableData=ref([])
const serviceId=ref('')
const searchFormData=reactive({
        pageIndex: 1,
        pageSize: 5
      })
const pageInfo=reactive({
        list: [],
        total: 0
      })
const routeList=ref([])
const defaultProps=reactive({
        children: 'children',
        label: 'label'
      })
      // dialog
const dlgTitle=ref('')
const limitDialogVisible=ref(false)
const limitDialogForm=ref()
const limitDialogFormData=reactive({
        id: 0,
        routeId: '',
        appKey: '',
        limitIp: '',
        limitKey: '',
        execCountPerSecond: 5,
        durationSeconds: 1,
        limitCode: '',
        limitMsg: '',
        tokenBucketCount: 5,
        limitStatus: 0, // 0: 停用，1：启用
        limitType: 1,
        orderIndex: 0,
        remark: '',
        typeKey: []
      })
const rulesLimit=reactive({
        typeKey: [
          { type: 'array', required: true, message: '请至少选择一个', trigger: 'change' }
        ],
        routeId: [
          { required: true, message: '不能为空', trigger: 'blur' },
          { min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
        ],
        appKey: [
          { required: true, message: '不能为空', trigger: 'blur' },
          { min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
        ],
        ip: [
          { required: true, message: '不能为空', trigger: 'blur' },
          { min: 1, max: 500, message: '长度在 1 到 500 个字符', trigger: 'blur' }
        ],
        // window
        execCountPerSecond: [
          { required: true, message: '不能为空', trigger: 'blur' }
        ],
        // token
        tokenBucketCount: [
          { required: true, message: '不能为空', trigger: 'blur' }
        ],
        orderIndex: [
          { required: true, message: '不能为空', trigger: 'blur' }
        ],
        remark: [
          { max: 128, message: '长度不能超过128字符', trigger: 'blur' }
        ]
      })
const loadTabs= async()=> {
  const resp=await request('/api/registry/service/list', {})
  tabsData.value=resp.data
  await nextTick()
  if (tabsData.value.length > 0) {
    tabsActive.value = tabsData.value[0]
    loadLimitData()
  }

}
// 加载树
const loadTree=async()=> {
  const resp=await request('/api/registry/service/list', {})
  treeData.value = convertToTreeData(respData, 0)
}
// 树搜索
const filterNode=(value, data)=> {
  if (!value) return true
  return data.label.indexOf(value) !== -1
}
// 树点击事件
const onNodeClick=(data, node, tree)=> {
  if (data.parentId) {
    serviceId.value = data.label
    searchFormData.serviceId = serviceId
    loadTable()
    loadRouteList(serviceId)
  }
}
const selectTab=()=> {
      loadLimitData()
    }
const loadLimitData=()=> {
  serviceId.value = tabsActive
  searchFormData.serviceId = serviceId
  loadTable()
  loadRouteList(serviceId)
}
/**
 * 数组转成树状结构
 * @param data 数据结构 [{
    "_parentId": 14,
    "gmtCreate": "2019-01-15 09:44:38",
    "gmtUpdate": "2019-01-15 09:44:38",
    "id": 15,
    "isShow": 1,
    "name": "用户注册",
    "orderIndex": 10000,
    "parentId": 14
},...]
  * @param pid 初始父节点id，一般是0
  * @return 返回结果 [{
  label: '一级 1',
  children: [{
    label: '二级 1-1',
    children: [{
      label: '三级 1-1-1'
    }]
  }]
}
*/
const  convertToTreeData=(data, pid)=>{
  const result = []
  const root = {
    label: data.length === 0 ? '无服务' : '服务列表',
    parentId: pid
  }
  const children = []
  for (let i = 0; i < data.length; i++) {
    const child = { parentId: 1, label: data[i] }
    children.push(child)
  }
  root.children = children
  result.push(root)
  return result
}
// table
const loadTable=async()=> {
  const resp=await request('/api/config/limit/list', searchFormData)
  Object.assign(pageInfo,resp.data)
}
const loadRouteList=async(serviceId)=> {
  const resp=await request('/api/route/list/1.2', { serviceId: serviceId }, function(resp) {
  routeList.value = resp.data
  })
}
const onAdd= ()=>{
  if (!serviceId.value) {
    useMessage().success('请选择服务', 'info')
    return
  }
  dlgTitle.value= '新增限流'
  limitDialogFormData.id = 0
  limitDialogVisible.value = true
}
const onSearchTable=()=> {
  searchFormData.pageIndex = 1
  loadTable()
}
const onTableUpdate=async(row)=> {
  dlgTitle.value = '修改限流'
  limitDialogVisible.value = true
  await nextTick()
  Object.assign(limitDialogFormData, row)
  if (row.routeId) {
    limitDialogFormData.typeKey.push(1)
  }
  if (row.appKey) {
    limitDialogFormData.typeKey.push(2)
  }
  if (row.limitIp) {
    limitDialogFormData.typeKey.push(3)
  }
}
const onLimitDialogClose= ()=> {
      limitDialogForm.value.resetFields();
      limitDialogFormData.limitStatus = 0
    }
const infoRender=(row)=> {
  if (row.limitType === 1) {
    const durationSeconds = row.durationSeconds
    return `每 ${durationSeconds} 秒可处理 ${row.execCountPerSecond} 个请求`
  } else if (row.limitType === 2) {
    return `令牌桶容量：${row.tokenBucketCount}`
  }
}
const onLimitDialogSave=()=>{
  limitDialogForm.value.validate(async(valid) => {
    if (valid) {
      cleanCheckboxData()
      limitDialogFormData.serviceId = serviceId
      const uri = limitDialogFormData.id ? 'config.limit.update' : 'config.limit.add'
      const resp=await request(uri, limitDialogFormData)
      limitDialogVisible.value = false
      loadTable()
    }
  })
}
const cleanCheckboxData=()=>{
  // 如果没有勾选则清空
  if (!checkTypeKey(1)) {
    limitDialogFormData.routeId = ''
  }
  if (!checkTypeKey(2)) {
    limitDialogFormData.appKey = ''
  }
  if (!checkTypeKey(3)) {
    limitDialogFormData.limitIp = ''
  }
}
const onLimitTypeTipClick=()=> {
  const windowRemark = '窗口策略：每秒处理固定数量的请求，超出请求数量返回错误信息。'
  const tokenRemark = '令牌桶策略：每秒放置固定数量的令牌数，每个请求进来后先去拿令牌，拿到了令牌才能继续，拿不到则等候令牌重新生成了再拿。'
  const content = windowRemark + '<br>' + tokenRemark
  useMessage().alert(content, '限流策略', {
    dangerouslyUseHTMLString: true
  })
}
const onSizeChange=(size) =>{
  searchFormData.pageSize = size
  loadTable()
}
const onPageIndexChange=(pageIndex)=> {
  searchFormData.pageIndex = pageIndex
  loadTable()
}
const checkTypeKey=(val) =>{
  return limitDialogFormData.typeKey.find((value, index, arr) => {
    return value === val
  })
}
const isWindowType=() =>{
      return limitDialogFormData.limitType === 1
    }
 const isTokenType= ()=> {
      return limitDialogFormData.limitType === 2
    }
const limitRender=(row)=> {
  const html = []
  const val = []
  if (row.routeId) {
    val.push(row.routeId)
    html.push('路由ID')
  }
  if (row.appKey) {
    val.push(row.appKey)
    html.push('AppId')
  }
  if (row.limitIp) {
    val.push(row.limitIp)
    html.push('IP')
  }
  return val.join(' + ') + '<br>(' + html.join(' + ') + ')'
}
onMounted(()=>{
  loadTabs()
})
watch(filterText,()=>{
  $refs.tree2.filter(val)
})

</script>
